import datetime
import os
import time

from ADBs import AndroidDevice


class MemoryMonitor:
    BASE_PATH = 'unknown'
    OOM_THR_UNKNOWN = 0
    OOM_THR_0 = 100000
    OOM_THR_1 = 250000  # 250000
    OOM_THR_2 = 300000  # 300000
    OOM_THR_3 = 350000
    MEMS_SIZE = 10

    @staticmethod
    def set_base_path():
        MemoryMonitor.BASE_PATH = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
        os.makedirs(MemoryMonitor.BASE_PATH)

    def __init__(self, dev: AndroidDevice):
        self.dev = dev
        self.mems = list()
        self.pid = None
        self.current_oom_threshold = MemoryMonitor.OOM_THR_UNKNOWN
        self.current_mem_kb = 0
        self.diagnose_count = 0
        self.hprofs = list()
        self.folder = MemoryMonitor.BASE_PATH + '/' + str(self.dev.id)
        os.makedirs(self.folder)

    def get_pid_of_dashcam(self):
        process_info = self.dev.adb.shell('shell \"ps | grep dashcam\"').split()
        if len(process_info) > 1:
            self.pid = process_info[1]
            return self.pid

    def diagnose(self):
        self.diagnose_count += 1
        if not self.pid:
            return
        self.save_mem_info()
        ret = self.dev.adb.shell('shell \"dumpsys meminfo com.askey.dvr.cdr7010.dashcam -d|grep \'TOTAL:\'\"').split()
        if len(ret) > 1:
            self.current_mem_kb = ret[1]
            self.mems.append(self.current_mem_kb)
            if len(self.mems) > MemoryMonitor.MEMS_SIZE:
                self.mems.pop(0)
            return self.current_mem_kb

    def gc(self):
        pid = self.get_pid_of_dashcam()
        if not pid:
            print(self.dev, 'Invalid pid')
            return
        self.dev.adb.shell('shell kill -10 %s' % pid)

    def save_mem_info(self):
        mem_file = './%s/%s_%skb_%d.mem' % (
            self.folder, self.dev.id, self.current_mem_kb, self.diagnose_count)
        self.dev.adb.shell('shell \"dumpsys meminfo com.askey.dvr.cdr7010.dashcam -d\" > %s' % mem_file)

    def dump_heap(self):
        self.gc()
        time.sleep(2)
        hprof_file = './%s/%s_%skb_%d.hprof' % (
            self.folder, self.dev.id, self.current_mem_kb, self.diagnose_count)
        self.dev.adb.shell('shell am dumpheap %s /data/data/temp.hprof' % self.pid)
        time.sleep(20)  # because dumpheap is async call.
        self.dev.adb.shell('pull /data/data/temp.hprof %s' % hprof_file)
        time.sleep(10)  # because dumpheap is async call.
        self.hprofs.append(hprof_file)

    def mem_check_oom(self):
        max_mem = int(max(self.mems))
        if max_mem > MemoryMonitor.OOM_THR_3:
            if self.current_oom_threshold < MemoryMonitor.OOM_THR_3:
                self.current_oom_threshold = MemoryMonitor.OOM_THR_3
                self.dump_heap()
            return MemoryMonitor.OOM_THR_3
        elif max_mem > MemoryMonitor.OOM_THR_2:
            if self.current_oom_threshold < MemoryMonitor.OOM_THR_2:
                self.current_oom_threshold = MemoryMonitor.OOM_THR_2
                self.dump_heap()
            return MemoryMonitor.OOM_THR_2
        elif max_mem > MemoryMonitor.OOM_THR_1:
            if self.current_oom_threshold < MemoryMonitor.OOM_THR_1:
                self.current_oom_threshold = MemoryMonitor.OOM_THR_1
                self.dump_heap()
            return MemoryMonitor.OOM_THR_1
        elif max_mem > MemoryMonitor.OOM_THR_0:
            if self.current_oom_threshold < MemoryMonitor.OOM_THR_0:
                self.current_oom_threshold = MemoryMonitor.OOM_THR_0
                self.dump_heap()
                return MemoryMonitor.OOM_THR_0

    def __str__(self):
        info = ('DEV:\t%s \n' % self.dev.__str__())
        info = info + ('CNT:\t%s\n' % self.diagnose_count)
        info = info + ('PID:\t%s\n' % self.pid)
        info = info + ('OOM:\t%s\n' % ('YES' if self.current_oom_threshold >= MemoryMonitor.OOM_THR_2 else 'NO'))
        info = info + ('MAX:\t%s Kb\n' % max(self.mems))
        info = info + ('MIN:\t%s Kb\n' % min(self.mems))
        info = info + ''.join('Mem:\t[%d] %s Kb\n' % (index, mem) for index, mem in enumerate(self.mems))
        if len(self.hprofs) > 0:
            info = info + ''.join('HER:\t%s\n' % path for path in self.hprofs)
        return info
